<!--
Copyright 2013 The Polymer Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
<!--
/**
 * @module Polymer Elements
 */
/**
 * g-ajax can be used to perform XMLHttpRequests.
 *
 * Example:
 *
 *     <g-ajax auto url="http://gdata.youtube.com/feeds/api/videos/" 
 *         params='{"alt":"json", "q":"chrome"}'
 *         handleAs="json"
 *         on-response="handleResponse">
 *     </g-ajax>
 *
 * @class g-ajax
 */
/**
 * Fired when a response is received.
 * 
 * @event response
 */
/**
 * Fired when an error is received.
 * 
 * @event error
 */
/**
 * Fired whenever a response or an error is received.
 *
 * @event complete
 */
-->
<link rel="import" href="g-xhr.html">
<polymer-element name="g-ajax" attributes="url handleAs auto params response">
  <template>
    <g-xhr id="xhr"></g-xhr>
    <content></content>
  </template>
  <script>
    Polymer('g-ajax', {
      /**
       * The URL target of the request.
       * 
       * @attribute url
       * @type string
       * @default ''
       */
      url: '',
      /**
       * Specifies what data to store in the 'response' property, and
       * to deliver as 'event.response' in 'response' events.
       * 
       * One of:
       * 
       *    `text`: uses XHR.responseText
       *    
       *    `xml`: uses XHR.responseXML
       *    
       *    `json`: uses XHR.responseText parsed as JSON
       *  
       * @attribute handleAs
       * @type string
       * @default 'text'
       */
      handleAs: '',
      /**
       * If true, automatically performs an Ajax request when either url or params has changed.
       *
       * @attribute auto
       * @type boolean
       * @default false
       */
      auto: false,
      /**
       * Parameters to send to the specified URL, as JSON.
       *  
       * @attribute params
       * @type string (JSON)
       * @default ''
       */
      params: '',
      /**
       * Returns the response object.
       *
       * @attribute response
       * @type Object
       * @default null
       */
      response: '',
      receive: function(inResponse, inXhr) {
        if (this.isSuccess(inXhr)) {
          this.processResponse(inXhr);
        } else {
          this.error(inXhr);
        }
        this.complete(inXhr);
      },
      isSuccess: function(inXhr) {
        var status = inXhr.status || 0;
        return !status || (status >= 200 && status < 300);
      },
      processResponse: function(inXhr) {
        var response = this.evalResponse(inXhr);
        this.response = response;
        this.fire('response', {response: response, xhr: inXhr});
      },
      error: function(inXhr) {
        var response = inXhr.status + ': ' + inXhr.responseText;
        this.fire('error', {response: response, xhr: inXhr});
      },
      complete: function(inXhr) {
        this.fire('complete', {response: inXhr.status, xhr: inXhr});
      },
      evalResponse: function(inXhr) {
        return this[(this.handleAs || 'text') + 'Handler'](inXhr);
      },
      xmlHandler: function(inXhr) {
        return inXhr.responseXML;
      },
      textHandler: function(inXhr) {
        return inXhr.responseText;
      },
      jsonHandler: function(inXhr) {
        var r = inXhr.responseText;
        try {
          return JSON.parse(r);
        } catch (x) {
          return r;
        }
      },
      urlChanged: function() {
        if (!this.handleAs) {
          var ext = String(this.url).split('.').pop();
          switch (ext) {
            case 'json':
              this.handleAs = 'json';
              break;
          }        
        }
        this.autoGo();
      },
      paramsChanged: function() {
        this.autoGo();
      },
      autoChanged: function() {
        this.autoGo();
      },
      // TODO(sorvell): Resolve more generally: property changes are
      // processed async all at once. In this case we have 2 properties that
      // should trigger one side effect. We could introduce another property
      // to manage this, but that would couple us to the property system
      // itself being async. Instead we resolve this manually via async
      // processing.
      autoGo: function() {
        if (this.auto) {
          clearTimeout(this._go);
          this._go = this.asyncMethod('go');
        }
      },
      /**
       * Performs an Ajax request to the url specified.
       *
       * @method go
       */
      go: function() {
        // don't go if there's no url
        if (!this.url) {
          return;
        }
        var params = this.params && typeof(this.params) == 'string' ? 
            JSON.parse(this.params) : this.params || null;
        return this.$.xhr.request({url: this.url, 
          callback: this.receive.bind(this), params: params});
      }
    });
  </script>
</polymer-element>
